﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;
using Noris.Schedule.Support.Expressions;
using Noris.Schedule.Support.Services;

namespace Noris.Schedule.UI.Expressions
{
    /// <summary>
    /// Třída, která integruje _ClassTree a RtfEditor za účelem editace textu s pomocí stromu tříd a atributů
    /// </summary>
    public class ExpressionEditPanel : Panel
    {
        #region Konstrukce
        public ExpressionEditPanel()
        {
            this.Init();
        }
        private void Init()
        {
            this._MainSplit = new SplitContainer();
            this._ClassTree = new Noris.Schedule.Support.Components.ClassTreePanel();
            this._SettingPanel = new VisualSettingPanel();
            this._RtfEditor = new RichTextBox();

            ((ISupportInitialize)(this._MainSplit)).BeginInit();
            this._MainSplit.Panel1.SuspendLayout();
            this._MainSplit.Panel2.SuspendLayout();
            this._MainSplit.SuspendLayout();
            ((ISupportInitialize)(this._ClassTree)).BeginInit();
            this._ClassTree.SuspendLayout();
            this.SuspendLayout();

            this._ClassTree.BorderStyle = System.Windows.Forms.BorderStyle.None;
            this._ClassTree.Dock = DockStyle.Fill;
            this._ClassTree.ButtonImage = Noris.Schedule.UI.Components.PicLibrary24.ArrowRightDouble_FromFile;
            this._ClassTree.ButtonText = "Select";
            this._ClassTree.TabIndex = 0;
            this._ClassTree.AttributeSelect += new Support.Components.ClassTreePanelHandler(_ClassTree_AttributeSelect);

            this._SettingPanel.Size = new Size(300, 56);
            this._SettingPanel.Dock = DockStyle.Bottom;
            this._SettingPanel.SettingChanged += new EventHandler(_SettingPanel_SettingChanged);
            this._SettingPanel.Visible = false;

            this._RtfEditor.AcceptsTab = true;
            this._RtfEditor.BorderStyle = System.Windows.Forms.BorderStyle.None;
            this._RtfEditor.Dock = DockStyle.Fill;
            this._RtfEditor.TabIndex = 1;
            this._RtfEditor.Text = "";
            this._RtfEditor.TextChanged += new EventHandler(_RtfEditor_TextChanged);

            this._MainSplit.BorderStyle = System.Windows.Forms.BorderStyle.None;
            this._MainSplit.FixedPanel = FixedPanel.Panel1;
            this._MainSplit.Dock = DockStyle.Fill;
            this._MainSplit.Panel1.Controls.Add(this._ClassTree);
            this._MainSplit.Panel2.Controls.Add(this._RtfEditor);
            this._MainSplit.Panel2.Controls.Add(this._SettingPanel);
            this._MainSplit.Size = new Size(800, 450);
            this._MainSplit.SplitterDistance = 400;

            this.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
            this.Size = new Size(800, 450);
            this.Controls.Add(this._MainSplit);

            this._MainSplit.Panel1.ResumeLayout(false);
            this._MainSplit.Panel2.ResumeLayout(false);
            ((ISupportInitialize)(this._MainSplit)).EndInit();
            this._MainSplit.ResumeLayout(false);
            ((ISupportInitialize)(this._ClassTree)).EndInit();
            this._ClassTree.ResumeLayout(false);
            this.ResumeLayout(false);

        }
        void _ClassTree_AttributeSelect(object sender, Support.Components.ClassTreePanelArgs args)
        {
            string insert = "";
            string source = args.SelectedClassNode.FullTreePath;
            foreach (Support.Components.ClassTreeAttributeItem attribute in args.SelectedAttributes)
            {
				string insertOne = attribute.ExpressionText;
                insert += (insert.Length == 0 ? "" : ", ") + insertOne;
            }
            this._RtfEditor.SelectedText = insert;
            this._RtfEditor.Focus();
        }
        void _SettingPanel_SettingChanged(object sender, EventArgs e)
        {
            this.OnVisualSettingChanged();
        }
        private SplitContainer _MainSplit;
        private Noris.Schedule.Support.Components.ClassTreePanel _ClassTree;
        private RichTextBox _RtfEditor;
        private VisualSettingPanel _SettingPanel;
        #endregion
        #region Public property a eventy
        /// <summary>
        /// Číslo třídy v rootu stromu
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int RootClassNumber
        {
            get { return this._ClassTree.RootClassNumber; }
            set { this._ClassTree.RootClassNumber = value; }
        }
        /// <summary>
        /// Objekt umožňující rozšiřovat strom tříd o další třídy, vztahy a atributy.
        /// Objekt je nutno setovat ještě před setováním RootClassNumber, 
        /// protože při vložení hodnoty do RootClassNumber se začínají načítat data pro tuto třídu,
        /// a v tu chvíli je třeba již mít přístup na ClassTreeExtender.
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public IClassTreeExtender ClassTreeExtender
        {
            get { return this._ClassTree.ClassTreeExtender; }
            set { this._ClassTree.ClassTreeExtender = value; }
        }
        /// <summary>
        /// Image na buttonu Select
        /// </summary>
        [Category("ExpressionEditor")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [Description("Image na buttonu Select")]
        public Image ButtonImage
        {
            get { return this._ClassTree.ButtonImage; }
            set { this._ClassTree.ButtonImage = value; }
        }
        /// <summary>
        /// Text na buttonu Select
        /// </summary>
        [Category("ExpressionEditor")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [Description("Text na buttonu Select")]
        public string ButtonText
        {
            get { return this._ClassTree.ButtonText; }
            set { this._ClassTree.ButtonText = value; }
        }
        /// <summary>
        /// Text (holý text, ne RTF) v editoru
        /// </summary>
        [Category("ExpressionEditor")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [Description("Text (holý text, ne RTF) v editoru")]
        public string EditedText
        {
            get { return this._RtfEditor.Text; }
            set { this._RtfEditor.Text = value; }
        }
        /// <summary>
        /// Text ve formátu RTF v editoru
        /// </summary>
        [Category("ExpressionEditor")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [Description("Text ve formátu RTF v editoru")]
        public string EditedTextRtf
        {
            get { return this._RtfEditor.Rtf; }
            set { this._RtfEditor.Rtf = value; }
        }
        /// <summary>
        /// Zoom v RTF editoru (lze jej nastavovat i kolečkem myši)
        /// </summary>
        [Category("ExpressionEditor")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [Description("Zoom v RTF editoru (lze jej nastavovat i kolečkem myši)")]
        public float RtfZoomFactor
        {
            get { return this._RtfEditor.ZoomFactor; }
            set { this._RtfEditor.ZoomFactor = value; }
        }
        /// <summary>
        /// Handler události Změna textu v editoru
        /// </summary>
		[Category("ExpressionEditor")]
		[Browsable(true)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Description("Handler události Změna textu v editoru")]
		public event EventHandler EditedTextChanged;
        /// <summary>
        /// Viditelnost panelu pro nastavení fontu a dalších vlastností
        /// </summary>
        [Category("ExpressionEditor")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Description("Viditelnost panelu pro nastavení fontu a dalších vlastností")]
        public bool VisualSettingVisible
        {
            get { return this._SettingPanel.Visible; }
            set { this._SettingPanel.Visible = value; }
        }
        /// <summary>
        /// Aktuální serializovaná hodnota v panelu pro nastavení fontu a dalších vlastností
        /// </summary>
        [Category("ExpressionEditor")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Description("Aktuální serializovaná hodnota v panelu pro nastavení fontu a dalších vlastností")]
        public string VisualSettingSerialized
        {
            get { return this._SettingPanel.Serialized; }
            set { this.SetVisualSetting(value, true); }
        }
        /// <summary>
        /// Handler události Změna nastavení (font, viditelnost)
        /// </summary>
        [Category("ExpressionEditor")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [Description("Handler události Změna nastavení (font, viditelnost)")]
        public event EventHandler VisualSettingChanged;
        #endregion
        #region Editace RTF textu, parsování, syntax coloring
        /// <summary>
        /// Změnila se hodnota textu v editoru. Mělo by proběhnout parsování a syntax coloring.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _RtfEditor_TextChanged(object sender, EventArgs e)
        {
            if (!this._SuppressRtfEditorTextChanged)
            {
                try
                {
                    this._SuppressRtfEditorTextChanged = true;
                    this.RtfTextParseAndColor();
                    if (this.EditedTextChanged != null)
                        this.EditedTextChanged(this, EventArgs.Empty);
                }
                finally
                {
                    this._SuppressRtfEditorTextChanged = false;
                }
            }
        }
        /// <summary>
        /// Zajistí syntaktické zpracování textu v RTF editoru
        /// </summary>
        private void RtfTextParseAndColor()
        {
            int selStart = this._RtfEditor.SelectionStart;
            string text = this._RtfEditor.Text;

            // RTF control do property Text vkládá konec řádku jako znak 0x0a (jen LF).
            // Parseru to nevadí, ale následně obsahuje taky jen LF a ne CR, a controly TextBox to nezobrazí dobře.
            // Proto LF rozšířím na CrLf:
            text = text.Replace("\n", "\r\n");

            string rtfText = RtfTextGetSyntaxColor(text);
            if (rtfText != null)
            {
                this._RtfEditor.Rtf = rtfText;
                this._RtfEditor.SelectionStart = selStart;
            }
        }
        /// <summary>
        /// Vrátí RTF text obsahující syntakticky zpracovaný vstupující holý text
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        private string RtfTextGetSyntaxColor(string text)
        {
            Noris.Schedule.Support.Parser.ParserSetting setting = ParserExpressionEditorSetting.GreenExpression;

            List<Noris.Schedule.Support.Parser.ParserSegment> segments = Noris.Schedule.Support.Parser.Parser.ParseString(text, setting);
            if (segments == null || segments.Count == 0) return null;

            return segments[0].RtfText;
        }
        /// <summary>
        /// true potlačí event _RtfEditor_TextChanged
        /// </summary>
        private bool _SuppressRtfEditorTextChanged;
        #endregion
        #region VisualSettings
        /// <summary>
        /// Vloží danou hodnotu do vizuálního nastavení (do _SettingPanel), volitelně potlačí vyvolání eventu this.VisualSettingChanged.
        /// </summary>
        /// <param name="serial"></param>
        /// <param name="silent"></param>
        protected void SetVisualSetting(string serial, bool silent)
        {
            bool isSuppress = this._SuppressVisualSettingChanged;
            try
            {
                this._SuppressVisualSettingChanged = silent;
                this._SettingPanel.Serialized = serial;
            }
            finally
            {
                this._SuppressVisualSettingChanged = isSuppress;
            }
        }
        protected virtual void OnVisualSettingChanged()
        {
            if (!this._SuppressVisualSettingChanged && this.VisualSettingChanged != null)
                this.VisualSettingChanged(this, EventArgs.Empty);
        }
        protected bool _SuppressVisualSettingChanged;
        #endregion
    }
}
